﻿using System;
using System.IO;
using System.Net;
using System.Threading;

namespace BTVPodcatcherNamespace
{
    public class DownloadManager
    {
        public delegate void DownloadFinishedDelegate(object sender, downloadFinishedEventArgs e);
        public event DownloadFinishedDelegate downloadFinished;
        private bool _usePool = false;
        private long _throttleAmount = 0;
        int _bufferSize = 4096;
        public static  int numberRunning =0;
        public DownloadManager()
        {

        }

        /// <summary>
        /// Starts the thread to perform the download.  One line of code
        /// executes the work method (PerformDownload) from a pooled thread.
        /// Comment it out and uncomment the other code to use an explicit
        /// thread.
        /// </summary>
        /// <param name="uri">The Uniform Resource Identifier to be downloaded</param>
        /// <param name="filename">The local filename for saving the remote file</param>
        public void StartDownload(DowloadInfo di, int throttleAmount)
        {
            

            
            if (_usePool)
            {
                //ThreadPool.QueueUserWorkItem(new WaitCallback(PerformDownload), entry);
            }
            else
            {
                numberRunning++;
                this._throttleAmount = throttleAmount*1024;
                // Create the thread, set it as a background, give it a name, and start it
                Thread downloadThread = new Thread(new ParameterizedThreadStart(PerformDownload));
                //downloadThread.IsBackground = true;
                downloadThread.Name = "BTV Podcatcher Downloader";
                downloadThread.Start(di);

                di.Tag = downloadThread;
            }

            
        }

        /// <summary>
        /// The actual downloading of the file.  The method loops multiple reads, setting
        /// status information and writing to the local file between each read.
        /// </summary>
        /// <param name="de">An instance of DownloadEntry</param>
        private void PerformDownload(object di)
        {
            DowloadInfo de = (DowloadInfo)di;
            


            
            int tickStart, tickEnd;
            if (de.DownloadStatus != DownloadStatus.CancelRequested)
                de.DownloadStatus = DownloadStatus.Downloading;
            de.BytesReceived = 0;
            string dlfileName = de.Destination.ToString();
            if (de.useTempFile)
                dlfileName = Path.GetTempFileName();
            Stream stream = null;
            FileStream fstream = null;
            de.ADownloadChangedOccured(new EventArgs());
            try
            {
                if (de.DownloadStatus != DownloadStatus.CancelRequested)
                {
                    WebRequest request = WebRequest.Create(de.DownloadUri);
                    if (request is HttpWebRequest)
                    {
                        ((HttpWebRequest)request).UserAgent = "BTV Podcatcher";
                        ((HttpWebRequest)request).Timeout = 1000000;
                    }

                    WebResponse response = request.GetResponse();
                    stream = response.GetResponseStream();
                    if (_throttleAmount > 0)
                    {
                        stream = new ThrottledStream(stream, _throttleAmount);
                    }
                    de.BytesToReceive = response.ContentLength;

                    fstream = File.Create(dlfileName, _bufferSize);//de.Destination.ToString(), _bufferSize);

                    int readBytes = -1;
                    byte[] inBuffer = new byte[_bufferSize];

                    // Exit the loop if a cancel is requested, or the file is completed downloaded
                    while (de.DownloadStatus != DownloadStatus.CancelRequested && readBytes != 0)
                    {
                        tickStart = Environment.TickCount;

                        // Read in 2K chunks
                        readBytes = stream.Read(inBuffer, 0, _bufferSize);

                        tickEnd = Environment.TickCount;

                        fstream.Write(inBuffer, 0, readBytes);

                        //elapsedTime = readEnd.Subtract(readStart);

                        // Calculate bytes/second
                        de.Speed = (long)((decimal)readBytes /
                            (Math.Max((decimal)1.0, tickEnd - tickStart) /
                            (decimal)1000.0));

                        de.BytesReceived += readBytes;
                        de.Progress = (int)(de.BytesReceived * 1.0 / de.BytesToReceive * 100.0);
                        if (de.BytesReceived > 1048576)
                        {
                            de.Message = string.Format("{0} of {1} mb downloaded", Math.Round(de.BytesReceived / 1048576.0, 2), Math.Round(de.BytesToReceive / 1048576.0, 2));
                        }
                        else if (de.BytesReceived > 10240 )
                        {
                            de.Message = string.Format("{0} of {1} kb downloaded", Math.Round(de.BytesReceived / 1024.0, 2), Math.Round(de.BytesToReceive / 1024.0, 2));
                        }
                        else
                        {
                            de.Message = string.Format("{0} of {1} bytes downloaded", de.BytesReceived, de.BytesToReceive);
                        }
                        de.LastUpdateTime = DateTime.Now;
                        de.ADownloadChangedOccured(new EventArgs());
                    }

                    
                    if (fstream != null)
                    {
                        fstream.Close();
                    }
                    if (stream != null)
                    {
                        stream.Close();
                    }
                    // The download has completed due to a cancel request or completion
                    if (de.DownloadStatus == DownloadStatus.CancelRequested)
                    {
                        request.Abort();
                        File.Delete(dlfileName);
                        de.DownloadStatus = DownloadStatus.Cancelled;
                        de.ADownloadChangedOccured(new EventArgs());
                    }
                    else
                    {
                        if (de.useTempFile)
                        {
                            FileInfo fi = de.Destination;
                            int count = 0;
                            while (fi.Exists)
                            {
                                fi = new FileInfo(fi.FullName.Substring(0, fi.FullName.Length - fi.Extension.Length) + "-" + count + fi.Extension);
                                count++;
                            }
                            de.Destination = fi;
                            File.Move(dlfileName, de.Destination.ToString());

                        }

                        de.DownloadStatus = DownloadStatus.Complete;
                        de.ADownloadChangedOccured(new EventArgs());
                        
                    }
                }
                else
                {
                    de.DownloadStatus = DownloadStatus.Cancelled;
                    de.ADownloadChangedOccured(new EventArgs());
                    
                }
            }
            catch (Exception e)
            {
                // If an error occured, make that clear
                de.DownloadStatus = DownloadStatus.Error;
                de.Message = e.Message;
                de.ADownloadChangedOccured(new EventArgs());
                

            }
            finally
            {
                // Always close resources at the end
                if (fstream != null)
                {
                    fstream.Close();
                }
                if (stream != null)
                {
                    stream.Close();
                }
            }
            numberRunning--;
            if (downloadFinished != null)
            {
                downloadFinished(this, new downloadFinishedEventArgs(de));
            }
        }

    }

    public class downloadFinishedEventArgs :EventArgs
    {
        private DowloadInfo _di;
        public downloadFinishedEventArgs(DowloadInfo dl)
        {
            _di = dl;
        }
        public DowloadInfo downloadedItem
        {
            get { return _di; }
        }
        

    }
}
